/*
 * UseCaseXML.java
 *
 * Created on 15 de abril de 2006, 13:14
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package usecase;

import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.Iterator;

// XML imports
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import org.apache.log4j.Logger;


/**
 * This class loads an XML file containing a use case
 * and offers several iterator to traverse the main and
 * alternative sequences.
 * This class is mainly used by ActivityDiagramGenerator.
 *
 * @author Rincew
 */
public class UseCaseXML 
{

    //--- Attributes -----------------------------------------------------
    protected Document useCaseDocument;
    protected StepBuilder stepBuilder;
    
    
    //--- Methods -----------------------------------------------------
    
    /** Creates a new instance of UseCaseXML */
    public UseCaseXML () {
        this.useCaseDocument = null;
        this.stepBuilder = new StepBuilder();
    }
    
    /** 
     * Loads a XML file with the definition of a use case 
     * This method assumes that file exists.
     **/
    public void load(File f) 
        throws UseCaseXMLException
    {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance ();
        DocumentBuilder dom;
        try {
            dom = domFactory.newDocumentBuilder();
            this.useCaseDocument = dom.parse (f);
        } catch (SAXException ex) {
            UseCaseXMLException exc = new UseCaseXMLException( "XML error " 
                + " when reading " + f +". \n" + ex.toString ()    );
            exc.setStackTrace (ex.getStackTrace ());
            throw exc;

        } catch (FileNotFoundException ex) {
            //ex.printStackTrace();
            UseCaseXMLException exc = new UseCaseXMLException( " " + 
                "File not found: >" + f +"< \n" );
            exc.setStackTrace (ex.getStackTrace ());
            throw exc;
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    
    /** Returns the tag <description>...</description> of 
     *  the use case template**/
    public String getUCDescription() {
        NodeList nList = useCaseDocument.getElementsByTagName ("description");
        Node n;
        if ( nList.getLength () == 0 )
            return "";
        n = nList.item (0);
        if (n.getFirstChild().getNodeType () != Node.TEXT_NODE) {
            return "";
        }

        return n.getFirstChild().getNodeValue ();

    }
    
    /**
     * Returns the ID atrtribute of the use case 
     **/
    public String getUCId() {
        NodeList nList = useCaseDocument.getElementsByTagName ("useCase");
        Node n;
        Node idNode;
        
        if ( nList.getLength () == 0 )
            return "";
        n = nList.item (0);
        
        idNode = n.getAttributes ().getNamedItem ("id");
        return idNode.getNodeValue ();

    }
    
    
    //--- Iterators methods ----------------------------------------------
    
    /**
     * Returns an iterator to traverse all the steps in the main sequence of
     *the use case
     **/
    public Iterator iteratorMainSequence() {
        return new IteratorMainSequence(this.useCaseDocument.getElementsByTagName ("step"));
    }

    /**
     * Returns an iterator to traverse all the pre steps in the alternative 
     * sequence of the use case of a given step.
     * For example, is step is the thrid step, this iterrator traverse
     * all the pre alternatives of the step 3.
     **/
    public Iterator iteratorPreSequence(IStep step) {
        return new IteratorPreSequence(this.useCaseDocument.getElementsByTagName ("alstep"), 
                                       step.getId ());
    }
    
    /**
     * Returns an iterator to traverse all the inv steps in the alternative 
     * sequence of the use case of a given step.
     **/
    public Iterator iteratorInvSequence(IStep step) {
        return new IteratorInvSequence(this.useCaseDocument.getElementsByTagName ("alstep"), 
                                       step.getId ());
        
    }
    
    	//--- Creation of iterators for alternative and error sections
    /**
     * 
     **/
    public Iterator iteratorAlternativeSequence(IStep step) {
        //return new IteratorMainSequence(
        	//this.useCaseDocument.getElementsByTagName ("astep"));
        NodeList listaNodos = this.useCaseDocument.getElementsByTagName ("astep");
        //System.out.println ("Paso: " + step.getId () + ":: nodos: " + listaNodos.getLength ());
        return new IteratorAltSequence(listaNodos,  step.getId ());
    }
    /**
     * 
     **/
    public Iterator iteratorErrorSequence(IStep step) {
        /*return new IteratorMainSequence(
        	this.useCaseDocument.getElementsByTagName ("estep"));*/
        return new IteratorErrSequence(this.useCaseDocument.getElementsByTagName ("estep"),  step.getId ());
    }

    public Iterator iteratorAlternativeMethodSequence(IStep step) {
        NodeList listaNodos = this.useCaseDocument.getElementsByTagName ("amethod");
        //System.out.println ("Paso: " + step.getId () + ":: nodos: " + listaNodos.getLength ());
        return new IteratorAltMethodSequence(listaNodos,  step.getId ());
    }

    public Iterator iteratorErrorMethodSequence(IStep step) {
        return new IteratorErrMethodSequence(this.useCaseDocument.getElementsByTagName ("emethod"),  step.getId ());
    }



    /** Testing purpouse only **/
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer("");
        Iterator it = this.iteratorMainSequence ();
        Iterator itPre, itInv;
        IStep step;
        
        while (it.hasNext ()) {
            step = (IStep) it.next ();
            sb.append (step+"\n");
            sb.append ("Pre alternatives;\n");
            itPre = this.iteratorPreSequence (step);
            while (itPre.hasNext ()) {
                step = (IStep) itPre.next ();
                sb.append (step + ": hasConclution?: " + 
                    step.hasConclution () + "\n");
            }
            sb.append ("Inv alternatives;\n");
            itPre = this.iteratorInvSequence (step);
            while (itPre.hasNext ()) {
                step = (IStep) itPre.next ();
                sb.append (step + ": hasConclution?: " + 
                    step.hasConclution () + "\n");
            }
        
        }
        
        sb.append ('\n');
        sb.append ("Participants: \n");
        //sb.append (this.stepBuilder.getParticipants ().toString ());
        
        return sb.toString ();
    }

     //--- Log ------------------------------------------------------------
    static Logger log;
    static {
        log = Logger.getLogger (UseCaseXML.class.getName());

    } 

    
    //--- Iterator classes -------------------------------
    
    private class IteratorMainSequence 
            implements Iterator
    {
        /** Stores the list of nodes in the main
         * sequence (tags with "step" name) */
        protected NodeList mainSequence;
        protected int mainSequenceIndex;

        public IteratorMainSequence(NodeList nodes) {
                this.mainSequenceIndex = 0;
                this.mainSequence = nodes;
        }
    
        public boolean hasNext () {
            return (this.mainSequenceIndex < this.mainSequence.getLength ());
        }

        public Object next () {
           Node n; 
           n = this.mainSequence.item (this.mainSequenceIndex++);
        
            return stepBuilder.builMainStep (n);
        }

        /** Operation not implemented */
        public void remove () {
            throw new java.lang.UnsupportedOperationException("Not impleented");
        }

    } // Class IteratorMainSequence


    //-----------------------------------------------------
    /**-- Helpper base class for pre an inv iterators --**/
    private class IteratorBase {
        /** Stores the ide of the main step **/
        String parentNode;

        /** Operation not implemented */
        public void remove () {
            throw new java.lang.UnsupportedOperationException("Not impleented");
        }

        /** Returns the type of the node by watching the 
         * last character of its ide */
        String getType(Node n) {
            String id;
            String type;
            Node aux;
            
            if (n == null)
                return null;
            if (n.getAttributes () == null) {
                log.warn ("Node without attributes.");
                return null;
            }
            
            aux = n.getAttributes ().getNamedItem ("id");
            if (aux == null) {
                log.warn ("Id attribute not found");
                return null;
            }
            id = aux.getNodeValue();
            type = String.valueOf (id.charAt (id.length ()-1));
            log.debug ("Type from id: " + id + " is '" + type+"'");
            
            if ( (type.compareTo ("p") !=0) &&
                 (type.compareTo ("i") !=0)
                )
                log.warn ("Type not p or i: " + aux);
            
            return type;
        }
        
        /** Returns true if the node describes a pre step **/
        public boolean isPre(Node n) {
            String type;
            
            type = this.getType (n);
            if (type == null)
                return false;
            return ( (type.compareTo ("pre") == 0) ||
                        (type.compareTo ("p") == 0) );
        }
        
        /** Returns true if the node describes a pre step **/
        public boolean isInv(Node n) {
            String type;
            
            type = this.getType (n);
            if (type == null)
                return false;
            return ( (type.compareTo ("inv") == 0) ||
                        (type.compareTo ("i") == 0) );
        }
        
        /** Helpper method **/
        boolean isNodeFromParent(Node n) {
            boolean isFromParent;
            String id;
            Node aux;
            
            if (n == null) {
                //log.warn ("Null node.");
                return false;
            }
            
            if (n.getAttributes () == null) {
                log.warn ("Node: " + n +" has no attributes but it must have an id attribute");
                return false;
            }
            
            aux = n.getAttributes ().getNamedItem ("id");
            if (aux == null) 
                return false;
            id = aux.getNodeValue ();
            
            if (this.parentNode == null)
                log.warn ("Parent node of id '"+id+"' is null.");
            
            isFromParent = ( StepBuilder.GetParentStep (id).compareTo(this.parentNode) == 0 );
            
            return isFromParent;
        }
        
    }


    /** ------------------------------------------- **/
    private class IteratorPreSequence 
            extends IteratorBase
            implements Iterator
    {
        /** Stores the list of nodes in the main
         * sequence (tags with "step" name) */
        protected NodeList preSequence;
        
       
        /** An index traverse nodes */
        protected int preSequenceIndex;

        public IteratorPreSequence(NodeList nodes, String s) {
                this.preSequenceIndex = 0;
                this.preSequence = nodes;
                this.parentNode = s;
        }
    
        public boolean hasNext () {
           Node n; 
           int i = 1;
           int tmpIndex = this.preSequenceIndex;
           
           if ( this.preSequence.getLength () == 0 )
               return false;
           if ( tmpIndex >= this.preSequence.getLength ())
               return false;
  
           do {
                n = this.preSequence.item (tmpIndex++);
                /*
                 * Node a;
                if (n == null)
                    System.err.println ("El nodo es nulo; "+i+" / "+tmpIndex + " / " + this.preSequence.getLength ());
                
                a = n.getAttributes ().getNamedItem ("type");
                if (a == null)
                    System.err.println ("El atributo es nulo; "+i);
                
                i++;
                */
           } while ( validNode(n)  &&
                   (tmpIndex <= (this.preSequence.getLength ()) ) ) ;
             
           
            // System.err.println ("Salida; "+i+" / "+tmpIndex + " / " + this.preSequence.getLength ());
            return ( (tmpIndex-1) < this.preSequence.getLength ());
        }

        /** **/
        public Object next () {
           Node n; 
           n = this.preSequence.item (this.preSequenceIndex++);
           
           while ( validNode(n) && 
                   (this.hasNext ()) )
               n = this.preSequence.item (this.preSequenceIndex++);
           
           return stepBuilder.builAlternativeStep (n);
        }

        
        /** Helpper method **/
        private boolean validNode(Node n) {
            return !( (this.isPre(n)) &&
                     (this.isNodeFromParent(n))     );
        }

    } // Class IteratorPreSequence

    
     /** ------------------------------------------- **/
    private class IteratorInvSequence 
            extends IteratorBase
            implements Iterator
    {
        /** Stores the list of nodes in the main
         * sequence (tags with "step" name) */
        protected NodeList preSequence;
        
        /** An index traverse nodes */
        protected int preSequenceIndex;

        public IteratorInvSequence(NodeList nodes, String s) {
                this.preSequenceIndex = 0;
                this.preSequence = nodes;
                this.parentNode = s;
                
                if (s == null)
                    log.warn ("Null parent.");
        }
    
        public boolean hasNext () {
           Node n, a; 
           int i = 1;
           int tmpIndex = this.preSequenceIndex;
           
           if ( this.preSequence.getLength () == 0 )
               return false;
           if ( tmpIndex >= this.preSequence.getLength ())
               return false;
  
           do {
                n = this.preSequence.item (tmpIndex++);
           } while ( validNode(n)  &&
                   (tmpIndex <= (this.preSequence.getLength ()) ) ) ;
             
           
            // System.err.println ("Salida; "+i+" / "+tmpIndex + " / " + this.preSequence.getLength ());
            return ( (tmpIndex-1) < this.preSequence.getLength ());
        }

        /** **/
        public Object next () {
           Node n; 
           n = this.preSequence.item (this.preSequenceIndex++);
           
           while ( validNode(n) && 
                   (this.hasNext ()) )
               n = this.preSequence.item (this.preSequenceIndex++);
           
           return stepBuilder.builAlternativeStep (n);
        }

                /** Helpper method **/
        private boolean validNode(Node n) {
            return !( (this.isInv(n)) &&
                     (this.isNodeFromParent(n))     );
        }
    } // Class IteratorPreSequence
    
    
    /** ------------------------------------------- **/
    private class IteratorAltSequence 
            extends IteratorBase
            implements Iterator
    {
        /** Stores the list of nodes in the main
         * sequence (tags with "step" name) */
        protected NodeList preSequence;
        
       
        /** An index traverse nodes */
        protected int preSequenceIndex;

        public IteratorAltSequence(NodeList nodes, String s) {
                this.preSequenceIndex = 0;
                this.preSequence = nodes;
                this.parentNode = s;
        }
    
        public boolean hasNext () {
           Node n, a; 
           int i = 1;
           int tmpIndex = this.preSequenceIndex;
           
           if ( this.preSequence.getLength () == 0 )
               return false;
           if ( tmpIndex >= this.preSequence.getLength ())
               return false;
  
           do {
                n = this.preSequence.item (tmpIndex++);
                
                /*if (n == null)
                    System.err.println ("El nodo es nulo; "+i+" / "+tmpIndex + " / " + this.preSequence.getLength ());*/
           } while ( validNode(n)  &&
                   (tmpIndex <= (this.preSequence.getLength ()) ) ) ;
             
             //System.err.println ("Salida; "+i+" / "+tmpIndex + " / " + this.preSequence.getLength ());
            return ( (tmpIndex-1) < this.preSequence.getLength ());
        }

        /** **/
        public Object next () {
           Node n; 
           n = this.preSequence.item (this.preSequenceIndex++);
           
           while ( validNode(n) && 
                   (this.hasNext ()) )
               n = this.preSequence.item (this.preSequenceIndex++);
           
           return stepBuilder.builAlternativeStep (n);
        }

        
        /** Helpper method **/
        private boolean validNode(Node n) {
            return !( /*(this.isPre(n)) &&*/
                     (this.isNodeFromParent(n))     );
        }

    } // Class IteratorPreSequence

    
         /** ------------------------------------------- **/
    private class IteratorErrSequence 
            extends IteratorBase
            implements Iterator
    {
        /** Stores the list of nodes in the main
         * sequence (tags with "step" name) */
        protected NodeList preSequence;
        
        /** An index traverse nodes */
        protected int preSequenceIndex;

        public IteratorErrSequence(NodeList nodes, String s) {
            super();
                this.preSequenceIndex = 0;
                this.preSequence = nodes;
                this.parentNode = s;
                
                if (s == null)
                    log.warn ("Null parent.");
        }
        /** **/
        public boolean hasNext () {
           Node n, a; 
           int i = 1;
           int tmpIndex = this.preSequenceIndex;
           
           if ( this.preSequence.getLength () == 0 )
               return false;
           if ( tmpIndex >= this.preSequence.getLength ())
               return false;
  
           do {
                n = this.preSequence.item (tmpIndex++);
           } while ( validNode(n)  &&
                   (tmpIndex <= (this.preSequence.getLength ()) ) ) ;
             
           
            // System.err.println ("Salida; "+i+" / "+tmpIndex + " / " + this.preSequence.getLength ());
            return ( (tmpIndex-1) < this.preSequence.getLength ());
        }

        /** **/
        public Object next () {
           Node n; 
           n = this.preSequence.item (this.preSequenceIndex++);
           IStep step;
           
           while ( validNode(n) && 
                   (this.hasNext ()) )
               n = this.preSequence.item (this.preSequenceIndex++);
           
           step = stepBuilder.builAlternativeStep (n);
           step.setType (IStep.INV);
           return step;
        }

                /** Helpper method **/
        private boolean validNode(Node n) {
            return !( /*(this.isInv(n)) &&*/
                     (this.isNodeFromParent(n))     );
        }

    } // Class IteratorErrSequence


    private class IteratorAltMethodSequence
            extends IteratorBase
            implements Iterator
    {
        /** Stores the list of nodes in the main
         * sequence (tags with "step" name) */
        protected NodeList preSequence;


        /** An index traverse nodes */
        protected int preSequenceIndex;

        public IteratorAltMethodSequence(NodeList nodes, String s) {
                this.preSequenceIndex = 0;
                this.preSequence = nodes;
                this.parentNode = s;
        }

        public boolean hasNext () {
           Node n;
           int tmpIndex = this.preSequenceIndex;

           if ( this.preSequence.getLength () == 0 )
               return false;
           if ( tmpIndex >= this.preSequence.getLength ())
               return false;

           do {
                n = this.preSequence.item (tmpIndex++);

           } while ( validNode(n)  &&
                   (tmpIndex <= (this.preSequence.getLength ()) ) ) ;

            return ( (tmpIndex-1) < this.preSequence.getLength ());
        }

        /** **/
        public Object next () {
           Node n;
           n = this.preSequence.item (this.preSequenceIndex++);

           while ( validNode(n) &&
                   (this.hasNext ()) )
               n = this.preSequence.item (this.preSequenceIndex++);

           return stepBuilder.builAlternativeMethodStep (n);
        }


        /** Helpper method **/
        private boolean validNode(Node n) {
            return !( /*(this.isPre(n)) &&*/
                     (this.isNodeFromParent(n))     );
        }

    } // Class IteratorPreSequence

    private class IteratorErrMethodSequence
            extends IteratorBase
            implements Iterator
    {
        /** Stores the list of nodes in the main
         * sequence (tags with "step" name) */
        protected NodeList preSequence;

        /** An index traverse nodes */
        protected int preSequenceIndex;

        public IteratorErrMethodSequence(NodeList nodes, String s) {
            super();
                this.preSequenceIndex = 0;
                this.preSequence = nodes;
                this.parentNode = s;

                if (s == null)
                    log.warn ("Null parent.");
        }
        /** **/
        public boolean hasNext () {
           Node n, a;
           int i = 1;
           int tmpIndex = this.preSequenceIndex;

           if ( this.preSequence.getLength () == 0 )
               return false;
           if ( tmpIndex >= this.preSequence.getLength ())
               return false;

           do {
                n = this.preSequence.item (tmpIndex++);
           } while ( validNode(n)  &&
                   (tmpIndex <= (this.preSequence.getLength ()) ) ) ;


            // System.err.println ("Salida; "+i+" / "+tmpIndex + " / " + this.preSequence.getLength ());
            return ( (tmpIndex-1) < this.preSequence.getLength ());
        }

        /** **/
        public Object next () {
           Node n;
           n = this.preSequence.item (this.preSequenceIndex++);
           IStep step;

           while ( validNode(n) &&
                   (this.hasNext ()) )
               n = this.preSequence.item (this.preSequenceIndex++);

           step = stepBuilder.builAlternativeMethodStep (n);
           step.setType (IStep.INV);
           return step;
        }

                /** Helpper method **/
        private boolean validNode(Node n) {
            return !( /*(this.isInv(n)) &&*/
                     (this.isNodeFromParent(n))     );
        }

    } // Class IteratorPreSequence


}
